package controller;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import model.CommonValues;
import model.Customer;
import model.Employee;
import model.Model;
import model.Order;
import model.Product;
import model.database.DBException;

public class SystemController {

	private Model model; // TODO CHECK NAME

	public SystemController(Model model) {
		
		// set member according to given value
		this.model = model;
	}
	
	/**
	 * Gets employee details and adds used an employee to store database.
	 * New employee's type if defined by the permission.
	 * Default user name is employee's number, and password is set to default password.
	 * Cannot add employee if there is another employee record with given number
	 * @param number- new employee's number
	 * @param name- new employee's name
	 * @param userName- new employee's user-name
	 * @param permission- new emploee's permission
	 * @return True if could add a new employee with given details, False if not
	 * @throws DBException if had errors connecting to database
	 */
	public boolean addEmployee(int number, String name, String userName,
			CommonValues.permissions permission) throws DBException {
		
		return model.addEmployee(number, name, userName, permission);
	}
	
	/**
	 * Creates a new account for customer matching given details.
	 * A new user-name record is created- default user name is customer's number.
	 * Cannot add customer if there is another customer record with given number
	 * @param number- new cutomer's number
	 * @param name- new cutomer's name
	 * @param address- new cutomer's address 
	 * @param userName- new customer's user-name
	 * @param paymentMethod- new cutomer's payment method
	 * @return True if could create customer account, False if not
	 * @throws DBException if had problems connecting to database
	 */
	public boolean createCustomerAccount(int number, String name, String userName,
			String address, CommonValues.paymentMethods paymentMethod)
			throws DBException {
		
		return model.createCustomerAccount(number, name, userName, address, paymentMethod);
	}
	
	/**
	 * Adds a new record with given user-name, permission and default password.
	 * Cannot add user if there is another user record with given user-name, password and permission
	 * @param userName- user-name for the new record
	 * @param permission- permission for the new record
	 * @return True if could add a new user-name record, False if not
	 * @throws DBException if had errors connecting to database
	 */
	public boolean addUserRecord(String userName,
			CommonValues.permissions permission) throws DBException {
		
		return model.addUserRecord(userName, permission);
	}

	/**
	 * Adds a new product with given name and price.
	 * Cannot add product if there is another product record with given name
	 * @param name- new product's name
	 * @param price- new product's price
	 * @return True if could add product, False if not
	 * @throws DBException if had problems connecting to database
	 */
	public boolean addNewProduct(String name, double price) throws DBException {
		
		return model.addNewProduct(name, price);
	}

	/**
	 * Adds a new order with given details. 
	 * Order's date is set to be current date.
	 * Cannot add order if there is another order record with given number,
	 * or if there is no product matching given name or no customer matching
	 * given number	 
	 * @param orderNumber- new order's number
	 * @param productName- name of product to order
	 * @param amount- how many items to order
	 * @param customerNumber- number of ordering customer
	 * @return True if could add order, False if not
	 * @throws DBException if had problems connecting to database
	 */
	public boolean addNewOrder(int orderNumber, String productName, int amount,
			int customerNumber) throws DBException {
		
		return model.addNewOrder(orderNumber, productName, amount, customerNumber);
	}

	/**
	 * Updates price of product with given name to given price.
	 * If there is no product with given name, returns False and
	 * no price is updated
	 * @param name- product to update's name
	 * @param newPrice- new price to set
	 * @return True if could update price, False if not
	 * @throws DBException if had problems connecting database
	 */
	public boolean updateProductPrice(String name, double newPrice)
			throws DBException {
		
		return model.updateProductPrice(name, newPrice);
	}

	/**
	 * Sets given details for customer matching given number.
	 * If there is no customer with given number, False is returned
	 * and no details are updated
	 * @param number- number of customer to update
	 * @param name- new name for customer
	 * @param address- new address for customer
	 * @param paymentMethod- new payment method for customer
	 * @return True if could update customer's details, False if not
	 * @throws DBException if had problems connecting database
	 */
	public boolean updateCustomerDetails(int number, String name, String address,
			CommonValues.paymentMethods paymentMethod) throws DBException {
		
		return model.updateCustomerDetails(number, name, address, paymentMethod);
	}

	/**
	 * Sets given password to given user. Password is set only if
	 * correct (old) password is given:
	 * composes MySQL query that changes password only if password value
	 * matching given user-name is given password
	 * Throws DBException if had problems connecting to database
	 */
	public boolean setPassword(String userName, String oldPassword, String newPassword)
			throws DBException {
		
		return model.setPassword(userName, oldPassword, newPassword);
	}
	
	/**
	 * Returns all products
	 * @return list of all products in system
	 * @throws DBException if had problems connecting database 
	 */
	public List<Map<String, String>> getProducts() throws DBException {
		
		// get all products
		List<Product> products = model.getProducts();
		
		// extract products details
		List<Map<String, String>> prodcutsDetails = extractProductDetails(products);
		
		// return extracted data
		return prodcutsDetails; 
	}

	/**
	 * Returns price of the product with given name
	 * @param productName- product's name to check
	 * @return product's name
	 * @throws DBException if had problems connecting to database,
	 * 					   or if product does not exists, 
	 * 					   or value stored as product's price is not 
	 * 					   a numeric value 
	 */
	public double getProductPrice(String productName) throws DBException {
		
		return model.getProductPrice(productName);
	}

	/**
	 * Returns all employees
	 * @return list of all store's employees
	 * @throws DBException if had problems connecting to database 
	 */
	public List<Map<String, String>> getEmployees() throws DBException {
		
		// get all employees
		List<Employee> employees = model.getEmployees();
		
		// extract employees details
		List<Map<String, String>> employeesDetails = extractEmployeeDetails(employees);
		
		// return extracted data
		return employeesDetails;
	}

	/**
	 * Returns all customers
	 * @return list of all store's registered customers
	 * @throws DBException if had problems connecting to database 
	 */
	public List<Map<String, String>> getCustomers() throws DBException {
		
		// get all customers
		List<Customer> customers = model.getCustomers();
		
		// extract customers details
		List<Map<String, String>> customersDetails = extractCustomersDetails(customers);
		
		// return extracted data
		return customersDetails;
	}

	/**
	 * Returns all customers that have ordered product matching given
	 * product name
	 * @return list of all customers that have ordered specified product
	 * @throws DBException if had problems connecting to database 
	 */
	public List<Map<String, String>> getCustomers(String productName)
			throws DBException {
		
		// get all customers
		List<Customer> customers = model.getCustomers(productName);
		
		// extract customers details
		List<Map<String, String>> customersDetails = extractCustomersDetails(customers);
		
		// return extracted data
		return customersDetails;
	}

	/**
	 * Returns all customers that have ordered products in worth greater than
	 * given value
	 * @param totalSum- lower bound for order's worth
	 * @return list of all customers that have ordered specified worth of products
	 * @throws DBException if had problems connecting to database
	 */
	public List<Map<String, String>> getCustomers(double totalSum)
			throws DBException {
		
		// get all customers
		List<Customer> customers = model.getCustomers(totalSum);
		
		// extract customers details
		List<Map<String, String>> customersDetails = extractCustomersDetails(customers);
		
		// return extracted data
		return customersDetails;
	}

	/**
	 * Returns orders made by customer matching given number
	 * @param customerNumber- customer number whose orders to search
	 * @return list of all orders made by specified customer
	 * @throws DBException if had problems connecting to database
	 */
	public List<Map<String, String>> getOrders(int customerNumber)
			throws DBException {
		
		// get all orders
		List<Order> orders = model.getOrders(customerNumber);
		
		// extract orders details
		List<Map<String, String>> ordersDetails = extractOrderDetails(orders);
		
		// return extracted data
		return ordersDetails;
	}

	/**
	 * Returns total sum of orders made on given date
	 * @param date- date to check, must be a string in format: "yyyy-mm-dd"
	 * @return total sum of orders made on given date
	 * @throws DBException if had problems connecting to database
	 */
	public double getSalesReport(String date) throws DBException {
		
		return model.getSalesReport(date);
	}

	/**
	 * Returns total sum of orders made on given month
	 * @param date- month to check
	 * @return total sum of orders made on given month
	 * @throws DBException if had problems connecting to database
	 */
	public double getSalesReport(int month) throws DBException {
		
		return model.getSalesReport(month);
	}
	
	/**
	 * Gets list of employees and for each employee creates a map
	 * holding the employee's parameters. Key names are defined by 
	 * CommonValue's members 
	 * @param employees- list of employees to process
	 * @return list of parameters map created for each employee
	 */
	public List<Map<String, String>> extractEmployeeDetails(
			List<Employee> employees) {
				
		// initialize list to hold parameters maps
		List<Map<String, String>> employeesDetails = new LinkedList<Map<String,String>>();
		
		// scan all given employees
		for (Employee currEmployee : employees) {
			
			// initialize map to hold current employee's parameters
			Map<String, String> currEmployeeParams = new HashMap<String, String>();
			
			// add all parameters
			currEmployeeParams.put(CommonValues.NUMBER, ""+currEmployee.getNumber());
			currEmployeeParams.put(CommonValues.NAME, currEmployee.getName());
			currEmployeeParams.put(CommonValues.USER_NAME, currEmployee.getUserName());
			
			// add employee's parameters to list
			employeesDetails.add(currEmployeeParams);
		}
		
		// return extracted details		
		return employeesDetails;
	}
	
	/**
	 * Gets list of customers and for each customer creates a map
	 * holding the customers's parameters. Key names are defined by 
	 * CommonValue's members 
	 * @param customers- list of customers to process
	 * @return list of parameters map created for each customer
	 */
	public List<Map<String, String>> extractCustomersDetails(
			List<Customer> customers) {
				
		// initialize list to hold parameters maps
		List<Map<String, String>> customersDetails = new LinkedList<Map<String,String>>();
		
		// scan all given customers
		for (Customer currCustomer : customers) {
			
			// initialize map to hold current customer's parameters
			Map<String, String> currCustomerParams = new HashMap<String, String>();
			
			// add all parameters
			currCustomerParams.put(CommonValues.NUMBER, ""+currCustomer.getNumber());
			currCustomerParams.put(CommonValues.NAME, currCustomer.getName());
			currCustomerParams.put(CommonValues.USER_NAME, currCustomer.getUserName());
			currCustomerParams.put(CommonValues.ADDRESS, currCustomer.getAddress());			
			currCustomerParams.put(CommonValues.PAYMENT_METHOD, currCustomer.getPaymentMethod().toString().toLowerCase());
			
			// add customer's parameters to list
			customersDetails.add(currCustomerParams);
		}
		
		// return extracted details		
		return customersDetails;
	}
	
	/**
	 * Gets list of products and for each product creates a map
	 * holding the product's parameters. Key names are defined by 
	 * CommonValue's members 
	 * @param products- list of products to process
	 * @return list of parameters map created for each product
	 */
	public List<Map<String, String>> extractProductDetails(
			List<Product> products) {
				
		// initialize list to hold parameters maps
		List<Map<String, String>> productsDetails = new LinkedList<Map<String,String>>();
		
		// scan all given products
		for (Product currProduct : products) {
			
			// initialize map to hold current product's parameters
			Map<String, String> currProductParams = new HashMap<String, String>();
			
			// add all parameters
			currProductParams.put(CommonValues.NAME, currProduct.getName());
			currProductParams.put(CommonValues.PRICE, ""+currProduct.getPrice());
			
			// add product's parameters to list
			productsDetails.add(currProductParams);
		}
		
		// return extracted details		
		return productsDetails;
	}
	
	/**
	 * Gets list of products and for each order creates a map
	 * holding the order's parameters. Key names are defined by 
	 * CommonValue's members 
	 * @param orders- list of orders to process
	 * @return list of parameters map created for each order
	 */
	public List<Map<String, String>> extractOrderDetails(
			List<Order> orders) {
				
		// initialize list to hold parameters maps
		List<Map<String, String>> ordersDetails = new LinkedList<Map<String,String>>();
		
		// scan all given orders
		for (Order currOrder : orders) {
			
			// initialize map to hold current order's parameters
			Map<String, String> currOrderParams = new HashMap<String, String>();
			
			// add all parameters
			currOrderParams.put(CommonValues.NUMBER, ""+currOrder.getOrderNumber());
			currOrderParams.put(CommonValues.DATE, currOrder.getDate());
			currOrderParams.put(CommonValues.ORDERING_CUSTOMER_NUMBER, ""+currOrder.getOrderingCustomerNumber());
			currOrderParams.put(CommonValues.NAME, ""+currOrder.getOrderedProductName());
			currOrderParams.put(CommonValues.AMOUNT, ""+currOrder.getAmount());
			currOrderParams.put(CommonValues.TOTAL_SUM, ""+currOrder.getTotalSum());
			
			// add order's parameters to list
			ordersDetails.add(currOrderParams);
		}
		
		// return extracted details		
		return ordersDetails;
	}
}